﻿using MongoDB.Driver;
using Sustainalytics.Entities.RiskRatings;
using Sustainalytics.MongoDB.Extensions;
using Sustainalytics.Synchronization.Interface;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sustainalytics.ClientDW.Synchronization.RiskRatingsBenchmarks
{
    public class RiskRatingsBenchmarkSync
    {
        private SyncContext _syncContext;

        private readonly IMongoCollection<CoreRiskRatingResearchProfile> _coreRiskRatingsCollection;
        private readonly IMongoCollection<RiskRatingResearchProfile> _riskRatingsCollection;
        private readonly IMongoCollection<RiskRatingBenchmark> _riskRatingsBenchMarksCollection;
        private readonly IMongoCollection<PeerAggregate> _riskRatingsHistogramCollection;
        private readonly IMongoCollection<RiskRatingMaterialEsgIssue> _materialEsgIssueCollection;
        private readonly IMongoCollection<RiskRatingMeiSubPeerGroupAverage> _meiSubPeerGroupAverageCollection;
        private readonly IMongoCollection<RiskRatingCluster> _clusterCollection;

        public RiskRatingsBenchmarkSync(
            IMongoCollection<CoreRiskRatingResearchProfile> coreRiskRatingsCollection,
            IMongoCollection<RiskRatingResearchProfile> riskRatingsCollection,
            IMongoCollection<RiskRatingBenchmark> riskRatingsBenchMarksCollection,
            IMongoCollection<PeerAggregate> riskRatingsHistogramCollection,
            IMongoCollection<RiskRatingMaterialEsgIssue> materialEsgIssueCollection,
            IMongoCollection<RiskRatingMeiSubPeerGroupAverage> meiSubPeerGroupAverageCollection,
            IMongoCollection<RiskRatingCluster> clusterCollection)
        {
            _coreRiskRatingsCollection = coreRiskRatingsCollection;
            _riskRatingsCollection = riskRatingsCollection;
            _riskRatingsBenchMarksCollection = riskRatingsBenchMarksCollection;
            _riskRatingsHistogramCollection = riskRatingsHistogramCollection;
            _materialEsgIssueCollection = materialEsgIssueCollection;
            _meiSubPeerGroupAverageCollection = meiSubPeerGroupAverageCollection;
            _clusterCollection = clusterCollection;
        }

        public void Start(SyncContext syncContext)
        {
            _syncContext = syncContext;
            _syncContext.Log("Begin computing benchmarks for Risk Ratings");

            var coreFields = Builders<CoreRiskRatingResearchProfile>.Projection
                .Include(x => x.Id)
                .Include(x => x.PeerGroup)
                .Include(x => x.SubPeerGroup)
                .Include(x => x.SubPeerGroupId)
                .Include(x => x.MarketCapitalization)
                .Include(x => x.ManagementRisk)
                .Include(x => x.Cluster)
                .Include(x => x.CompanyName);

            var fields = Builders<RiskRatingResearchProfile>.Projection
                .Include(x => x.Id)
                .Include(x => x.PeerGroup)
                .Include(x => x.SubPeerGroup)
                .Include(x => x.SubPeerGroupId)
                .Include(x => x.MarketCapitalization)
                .Include(x => x.ManagementRisk)
                .Include(x => x.Cluster)
                .Include(x => x.CompanyName);

            var coreRiskRatingsData = _coreRiskRatingsCollection.Find(_ => true)
                .Project<CoreRiskRatingResearchProfile>(coreFields)
                .ToEnumerable()
                .Select(x => new RiskRatingResearchProfile
                {
                    Id = x.Id,
                    PeerGroup = x.PeerGroup,
                    SubPeerGroup = x.SubPeerGroup,
                    SubPeerGroupId = x.SubPeerGroupId,
                    MarketCapitalization = x.MarketCapitalization,
                    ManagementRisk = x.ManagementRisk,
                    Cluster = x.Cluster,
                    CompanyName = x.CompanyName
                })
                .ToList();

            var comprehensiveRiskRatingsData = _riskRatingsCollection.Find(_ => true)
                .Project<RiskRatingResearchProfile>(fields)
                .ToList();

            var riskRatingsData = coreRiskRatingsData.Union(comprehensiveRiskRatingsData, new RiskRatingResearchProfileComparer()).ToList();

            var subPeerGroupIds = riskRatingsData.GroupBy(x => x.SubPeerGroupId).Select(x => x.Key).ToList();




            LogExecutionTimeAsync("Delete companies benchmarks collection", async () => 
                {
                    await _riskRatingsBenchMarksCollection.DeleteManyAsync(_ => true).ConfigureAwait(false);
                })
                .Wait();

            LogExecutionTimeAsync("Compute companies benchmarks", async () => 
                {
                    await GetCompanyBenchmarks(_riskRatingsBenchMarksCollection, _materialEsgIssueCollection, riskRatingsData).ConfigureAwait(false);
                })
                .Wait();

            LogExecutionTimeAsync("Delete histograms collection", async () => 
                {
                    await _riskRatingsHistogramCollection.DeleteManyAsync(_ => true).ConfigureAwait(false);
                })
                .Wait();

            LogExecutionTimeAsync("Compute universe histograms", async () => 
                {
                    await GetUniverseHistogram(_riskRatingsHistogramCollection, _clusterCollection, riskRatingsData).ConfigureAwait(false);
                }).Wait();

            LogExecutionTimeAsync("Compute Peer groups histograms", async () => 
                {
                    await GetPeerGroupHistograms(_riskRatingsHistogramCollection, _clusterCollection, riskRatingsData).ConfigureAwait(false);
                }).Wait();

            LogExecutionTimeAsync("Compute Universe MEI histograms", async () => 
                {
                    await GetUniverseMEIHistograms(_riskRatingsHistogramCollection, _materialEsgIssueCollection, _clusterCollection).ConfigureAwait(false);
                })
                .Wait();
            LogExecutionTimeAsync("Compute Industry MEI histograms", async () => 
                {
                    await GetIndustryMEIHistograms(_riskRatingsHistogramCollection, _materialEsgIssueCollection, _clusterCollection, riskRatingsData).ConfigureAwait(false);
                })
                .Wait();
            LogExecutionTimeAsync("Compute SubIndustryMEI histograms", async () => 
                {
                    await GetSubIndustryMEIHistograms(_riskRatingsHistogramCollection, _materialEsgIssueCollection, _clusterCollection, riskRatingsData).ConfigureAwait(false);
                })
                .Wait();

            LogExecutionTimeAsync("Delete MEI Average scores", async () => 
                {
                    await _meiSubPeerGroupAverageCollection.DeleteManyAsync(_ => true).ConfigureAwait(false);
                })
                .Wait();

            LogExecutionTimeAsync("Compute MEI Average scores", async () => 
                {
                    await GetMeiAveragePerSubIndustry(_meiSubPeerGroupAverageCollection, _materialEsgIssueCollection, subPeerGroupIds).ConfigureAwait(false);
                })
                .Wait();

            _syncContext.Log("End computing benchmarks for Risk Ratings");
        }

        private static async Task GetMeiAveragePerSubIndustry(
            IMongoCollection<RiskRatingMeiSubPeerGroupAverage> meiSubPeerGroupAverageCollection,
            IMongoCollection<RiskRatingMaterialEsgIssue> subIndustryMeiCollection,
            List<int> subPeerGroupIds)
        {
            var averageList = new List<RiskRatingMeiSubPeerGroupAverage>();

            subPeerGroupIds.ForEach((comparativeGroup) =>
            {
                var companiesWithMEI = subIndustryMeiCollection
                    .Find(x => x.SubPeerGroupId == comparativeGroup && x.Type != MaterialIssueType.IdiosyncraticIssue)
                    .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                        .Include(x => x.SubPeerGroupId)
                        .Include(x => x.ExternalId)
                        .Include(x => x.ManagementRisk.UnmanagedRisk.Value)
                        .Include(x => x.ManagementRisk.Exposure.Value)
                        .Include(x => x.ManagementRisk.Management.Value)
                        .Include(x => x.ManageableRiskFactorPercentile.Value)
                        .Include(x => x.ExcessExposure)
                        .Include(x => x.SubIndustryExposureScore))
                    .ToListAsync().Result;

                companiesWithMEI.GroupBy(x => x.ExternalId, x => x).Select(x => x.Key).ToList().ForEach((externalId) =>
                {
                    averageList.Add(new RiskRatingMeiSubPeerGroupAverage()
                    {
                        ExternalId = externalId,
                        SubPeerGroupId = comparativeGroup,
                        ExposureAverage = (int)companiesWithMEI.Where(x => x.ExternalId == externalId)
                            .DefaultIfEmpty(new RiskRatingMaterialEsgIssue()).Average(x => x.ManagementRisk.Exposure.Value),
                        ManagementRiskAverage = (int)companiesWithMEI.Where(x => x.ExternalId == externalId)
                            .DefaultIfEmpty(new RiskRatingMaterialEsgIssue()).Average(x => x.ManagementRisk.Management.Value),
                        UnmanagedRiskAverage = (int)companiesWithMEI.Where(x => x.ExternalId == externalId)
                            .DefaultIfEmpty(new RiskRatingMaterialEsgIssue()).Average(x => x.ManagementRisk.UnmanagedRisk.Value),
                        ExcessExposureAverage = (int)companiesWithMEI.Where(x => x.ExternalId == externalId)
                            .DefaultIfEmpty(new RiskRatingMaterialEsgIssue()).Average(x => x.ExcessExposure.Value),
                        ManageableRiskFactorAverage = (int)companiesWithMEI.Where(x => x.ExternalId == externalId)
                            .DefaultIfEmpty(new RiskRatingMaterialEsgIssue()).Average(x => x.ManageableRiskFactorPercentile.Value),
                        SubIndustryExposureAverage = (int)companiesWithMEI.Where(x => x.ExternalId == externalId)
                            .DefaultIfEmpty(new RiskRatingMaterialEsgIssue()).Average(x => x.SubIndustryExposureScore.Value),

                    });
                });
            });

            await meiSubPeerGroupAverageCollection.InsertManyAsync(averageList);
        }

        private static Dictionary<Guid, Dictionary<int, RiskRatingRelativePerformance>> GetMeiUniverseRelativePerformances(List<RiskRatingResearchProfile> companyProfiles,
            IMongoCollection<RiskRatingMaterialEsgIssue> materialEsgIssueCollection, string benchmarktype)
        {
            var externalIds = materialEsgIssueCollection
                .Find(x => x.Type != MaterialIssueType.IdiosyncraticIssue)
                .Project(x => x.ExternalId)
                .ToList()
                .Distinct();

            var comparativeGroups = new Dictionary<int, ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, string>>();
            var relativePerformanceDictionary = new Dictionary<Guid, Dictionary<int, RiskRatingRelativePerformance>>();

            foreach (var externalId in externalIds)
            {

                if (benchmarktype == "Risk")
                {
                    var mei = materialEsgIssueCollection
                        .Find(x => x.ExternalId == externalId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                        .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                            .Include(x => x.CompanyId)
                            .Include(x => x.PeerGroup)
                            .Include(x => x.ManagementRisk.UnmanagedRisk.Value))
                        .ToList();

                    var comparativeGroup = ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, string>
                        .Create(mei, x => x.CompanyId, null, null, null, x => x.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer());
                    comparativeGroups.Add(externalId, comparativeGroup);

                }
                else if (benchmarktype == "Exposure")
                {
                    var mei = materialEsgIssueCollection
                          .Find(x => x.ExternalId == externalId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                          .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                              .Include(x => x.CompanyId)
                              .Include(x => x.PeerGroup)
                              .Include(x => x.ManagementRisk.Exposure.Value))
                          .ToList();

                    var comparativeGroup = ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, string>
                       .Create(mei, x => x.CompanyId, null, null, null, x => x.ManagementRisk.Exposure.Value, new DoubleComparer());
                    comparativeGroups.Add(externalId, comparativeGroup);
                }
                else
                {
                    var mei = materialEsgIssueCollection
                          .Find(x => x.ExternalId == externalId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                          .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                          .Include(x => x.CompanyId)
                          .Include(x => x.PeerGroup)
                          .Include(x => x.ManagementRisk.Management.Value))
                          .ToList();

                    var comparativeGroup = ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, string>
                       .Create(mei, x => x.CompanyId, null, null, null, x => x.ManagementRisk.Management.Value, new DoubleComparer());
                    comparativeGroups.Add(externalId, comparativeGroup);
                }
            }

            foreach (var companyProfile in companyProfiles)
            {
                var meiDictionary = new Dictionary<int, RiskRatingRelativePerformance>();

                var materialEsgIssues = materialEsgIssueCollection
                    .Find(x => x.CompanyId == companyProfile.Id && x.Type != MaterialIssueType.IdiosyncraticIssue)
                    .Project(x => x.ExternalId)
                    .ToList();

                if (materialEsgIssues.Any())
                {
                    foreach (var externalId in materialEsgIssues)
                    {
                        var comparativeGroup = comparativeGroups.ContainsKey(externalId)
                                ? comparativeGroups[externalId]
                                : null;

                        if (comparativeGroup != null)
                        {
                            var benchmark = comparativeGroup.GetBenchmark(companyProfile.Id);

                            meiDictionary.Add(externalId, new RiskRatingRelativePerformance
                            {
                                Count = benchmark.Count,
                                Percentile = benchmark.Percentile,
                                Rank = benchmark.Rank,
                                Average = benchmark.Average
                            });
                        }
                    }
                }

                relativePerformanceDictionary.Add(companyProfile.Id, meiDictionary);
            }

            return relativePerformanceDictionary;
        }

        private static Dictionary<Guid, Dictionary<int, RiskRatingRelativePerformance>> GetMeiPeerGroupRelativePerformances(List<RiskRatingResearchProfile> companyProfiles,
            IMongoCollection<RiskRatingMaterialEsgIssue> materialEsgIssueCollection, string benchmarktype)
        {
            var externalIds = materialEsgIssueCollection
                .Find(x => x.Type != MaterialIssueType.IdiosyncraticIssue)
                .Project(x => x.ExternalId)
                .ToList()
                .Distinct();

            var comparativeGroups = new Dictionary<int, ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, string>>();

            var relativePerformanceDictionary = new Dictionary<Guid, Dictionary<int, RiskRatingRelativePerformance>>();

            foreach (var externalId in externalIds)
            {
                if (benchmarktype == "Risk")
                {
                    var mei = materialEsgIssueCollection
                             .Find(x => x.ExternalId == externalId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                             .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                                 .Include(x => x.CompanyId)
                                 .Include(x => x.PeerGroup)
                                  .Include(x => x.ManagementRisk.UnmanagedRisk.Value))
                             .ToList();

                    var comparativeGroup = ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, string>
                        .Create(mei, x => x.CompanyId, null, x => x.PeerGroup, new PeerGroupComparer(), x => x.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer());
                    comparativeGroups.Add(externalId, comparativeGroup);

                }
                else if (benchmarktype == "Exposure")
                {
                    var mei = materialEsgIssueCollection
                            .Find(x => x.ExternalId == externalId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                            .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                                .Include(x => x.CompanyId)
                                .Include(x => x.PeerGroup)
                                 .Include(x => x.ManagementRisk.Exposure.Value))
                            .ToList();

                    var comparativeGroup = ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, string>
                        .Create(mei, x => x.CompanyId, null, x => x.PeerGroup, new PeerGroupComparer(), x => x.ManagementRisk.Exposure.Value, new DoubleComparer());
                    comparativeGroups.Add(externalId, comparativeGroup);
                }
                else
                {
                    var mei = materialEsgIssueCollection
                             .Find(x => x.ExternalId == externalId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                             .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                                 .Include(x => x.CompanyId)
                                 .Include(x => x.PeerGroup)
                                  .Include(x => x.ManagementRisk.Management.Value))
                             .ToList();

                    var comparativeGroup = ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, string>
                        .Create(mei, x => x.CompanyId, null, x => x.PeerGroup, new PeerGroupComparer(), x => x.ManagementRisk.Management.Value, new DoubleComparer());
                    comparativeGroups.Add(externalId, comparativeGroup);
                }
            }

            foreach (var companyProfile in companyProfiles)
            {
                var meiDictionary = new Dictionary<int, RiskRatingRelativePerformance>();

                var materialEsgIssues = materialEsgIssueCollection
                    .Find(x => x.CompanyId == companyProfile.Id && x.Type != MaterialIssueType.IdiosyncraticIssue)
                    .Project(x => x.ExternalId)
                    .ToList();

                if (materialEsgIssues.Any())
                {
                    foreach (var externalId in materialEsgIssues)
                    {
                        var comparativeGroup = comparativeGroups.ContainsKey(externalId)
                                ? comparativeGroups[externalId]
                                : null;

                        if (comparativeGroup != null)
                        {
                            var benchmark = comparativeGroup.GetBenchmark(companyProfile.Id);

                            meiDictionary.Add(externalId, new RiskRatingRelativePerformance
                            {
                                Count = benchmark.Count,
                                Percentile = benchmark.Percentile,
                                Rank = benchmark.Rank,
                                Average = benchmark.Average
                            });
                        }
                    }
                }

                relativePerformanceDictionary.Add(companyProfile.Id, meiDictionary);
            }

            return relativePerformanceDictionary;
        }

        private static Dictionary<Guid, Dictionary<int, RiskRatingRelativePerformance>> GetMeiSubPeerGroupRelativePerformances(List<RiskRatingResearchProfile> companyProfiles,
            IMongoCollection<RiskRatingMaterialEsgIssue> materialEsgIssueCollection, string benchmarktype)
        {
            var externalIds = materialEsgIssueCollection
                .Find(x => x.Type != MaterialIssueType.IdiosyncraticIssue)
                .Project(x => x.ExternalId)
                .ToList()
                .Distinct();

            var comparativeGroups = new Dictionary<int, ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, int>>();

            var relativePerformanceDictionary = new Dictionary<Guid, Dictionary<int, RiskRatingRelativePerformance>>();

            foreach (var externalId in externalIds)
            {
                if (benchmarktype == "Risk")
                {
                    var mei = materialEsgIssueCollection
                            .Find(x => x.ExternalId == externalId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                            .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                                .Include(x => x.CompanyId)
                                .Include(x => x.SubPeerGroupId)
                                .Include(x => x.ManagementRisk.UnmanagedRisk.Value))
                            .ToList();

                    var comparativeGroup = ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, int>
                    .Create(mei, x => x.CompanyId, null, x => x.SubPeerGroupId, new SubPeerGroupComparer(), x => x.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer());
                    comparativeGroups.Add(externalId, comparativeGroup);
                }
                else if (benchmarktype == "Exposure")
                {
                    var mei = materialEsgIssueCollection
                           .Find(x => x.ExternalId == externalId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                           .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                               .Include(x => x.CompanyId)
                               .Include(x => x.SubPeerGroupId)
                               .Include(x => x.ManagementRisk.Exposure.Value))
                           .ToList();

                    var comparativeGroup = ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, int>
                    .Create(mei, x => x.CompanyId, null, x => x.SubPeerGroupId, new SubPeerGroupComparer(), x => x.ManagementRisk.Exposure.Value, new DoubleComparer());
                    comparativeGroups.Add(externalId, comparativeGroup);
                }
                else
                {
                    var mei = materialEsgIssueCollection
                            .Find(x => x.ExternalId == externalId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                            .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                                .Include(x => x.CompanyId)
                                .Include(x => x.SubPeerGroupId)
                                .Include(x => x.ManagementRisk.Management.Value))
                            .ToList();

                    var comparativeGroup = ComparativeGroup<RiskRatingMaterialEsgIssue, Guid, int>
                    .Create(mei, x => x.CompanyId, null, x => x.SubPeerGroupId, new SubPeerGroupComparer(), x => x.ManagementRisk.Management.Value, new DoubleComparer());
                    comparativeGroups.Add(externalId, comparativeGroup);
                }
            }

            foreach (var companyProfile in companyProfiles)
            {
                var meiDictionary = new Dictionary<int, RiskRatingRelativePerformance>();

                var materialEsgIssues = materialEsgIssueCollection
                    .Find(x => x.CompanyId == companyProfile.Id && x.Type != MaterialIssueType.IdiosyncraticIssue)
                    .Project(x => x.ExternalId)
                    .ToList();

                if (materialEsgIssues.Any())
                {
                    foreach (var externalId in materialEsgIssues)
                    {
                        var comparativeGroup = comparativeGroups.ContainsKey(externalId)
                                ? comparativeGroups[externalId]
                                : null;

                        if (comparativeGroup != null)
                        {
                            var benchmark = comparativeGroup.GetBenchmark(companyProfile.Id);

                            meiDictionary.Add(externalId, new RiskRatingRelativePerformance
                            {
                                Count = benchmark.Count,
                                Percentile = benchmark.Percentile,
                                Rank = benchmark.Rank,
                                Average = benchmark.Average
                            });
                        }
                    }
                }

                relativePerformanceDictionary.Add(companyProfile.Id, meiDictionary);
            }

            return relativePerformanceDictionary;
        }

        private static async Task GetCompanyBenchmarks(IMongoCollection<RiskRatingBenchmark> riskRatingsBenchMarksCollection,
            IMongoCollection<RiskRatingMaterialEsgIssue> materialEsgIssueCollection,
            List<RiskRatingResearchProfile> riskRatingsData)
        {
            var peerGroupComparativeGroupRisk = ComparativeGroup<RiskRatingResearchProfile, Guid, string>
                .Create(riskRatingsData, x => x.Id, x => x.CompanyName, x => x.PeerGroup, new PeerGroupComparer(), x => x.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer());
            var subPeerGroupComparativeGroupRisk = ComparativeGroup<RiskRatingResearchProfile, Guid, int>
                .Create(riskRatingsData, x => x.Id, x => x.CompanyName, x => x.SubPeerGroupId, new SubPeerGroupComparer(), x => x.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer());
            var universeComparativeGroupRisk = ComparativeGroup<RiskRatingResearchProfile, Guid, int>
                .Create(riskRatingsData, x => x.Id, x => x.CompanyName, null, null, x => x.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer());
            var peerGroupComparativeGroupExposure = ComparativeGroup<RiskRatingResearchProfile, Guid, string>
                .Create(riskRatingsData, x => x.Id, x => x.CompanyName, x => x.PeerGroup, new PeerGroupComparer(), x => x.ManagementRisk.Exposure.Value, new DoubleComparer());
            var subPeerGroupComparativeGroupExposure = ComparativeGroup<RiskRatingResearchProfile, Guid, int>
                .Create(riskRatingsData, x => x.Id, x => x.CompanyName, x => x.SubPeerGroupId, new SubPeerGroupComparer(), x => x.ManagementRisk.Exposure.Value, new DoubleComparer());
            var universeComparativeGroupExposure = ComparativeGroup<RiskRatingResearchProfile, Guid, int>
                .Create(riskRatingsData, x => x.Id, x => x.CompanyName, null, null, x => x.ManagementRisk.Exposure.Value, new DoubleComparer());
            var peerGroupComparativeGroupManagement = ComparativeGroup<RiskRatingResearchProfile, Guid, string>
                .Create(riskRatingsData, x => x.Id, x => x.CompanyName, x => x.PeerGroup, new PeerGroupComparer(), x => x.ManagementRisk.Management.Value, new DoubleComparer());
            var subPeerGroupComparativeGroupManagement = ComparativeGroup<RiskRatingResearchProfile, Guid, int>
                .Create(riskRatingsData, x => x.Id, x => x.CompanyName, x => x.SubPeerGroupId, new SubPeerGroupComparer(), x => x.ManagementRisk.Management.Value, new DoubleComparer());
            var universeComparativeGroupManagement = ComparativeGroup<RiskRatingResearchProfile, Guid, int>
                .Create(riskRatingsData, x => x.Id, x => x.CompanyName, null, null, x => x.ManagementRisk.Management.Value, new DoubleComparer());

            var marketCapitalizationComputingRisk = new MarketCapBenchmark<RiskRatingResearchProfile, Guid, int>(x => x.MarketCapitalization, subPeerGroupComparativeGroupRisk, new CustomComparer());
            var marketCapitalizationComputingExposure = new MarketCapBenchmark<RiskRatingResearchProfile, Guid, int>(x => x.MarketCapitalization, subPeerGroupComparativeGroupExposure, new CustomComparer());
            var marketCapitalizationComputingManagement = new MarketCapBenchmark<RiskRatingResearchProfile, Guid, int>(x => x.MarketCapitalization, subPeerGroupComparativeGroupManagement, new CustomComparer());

            var meiUniverseRelativePerformancesRisk = GetMeiUniverseRelativePerformances(riskRatingsData, materialEsgIssueCollection, "Risk");
            var meiPeerGroupRelativePerformancesRisk = GetMeiPeerGroupRelativePerformances(riskRatingsData, materialEsgIssueCollection, "Risk");
            var meiSubPeerGroupRelativePerformancesRisk = GetMeiSubPeerGroupRelativePerformances(riskRatingsData, materialEsgIssueCollection, "Risk");

            var meiUniverseRelativePerformancesExposure = GetMeiUniverseRelativePerformances(riskRatingsData, materialEsgIssueCollection, "Exposure");
            var meiPeerGroupRelativePerformancesExposure = GetMeiPeerGroupRelativePerformances(riskRatingsData, materialEsgIssueCollection, "Exposure");
            var meiSubPeerGroupRelativePerformancesExposure = GetMeiSubPeerGroupRelativePerformances(riskRatingsData, materialEsgIssueCollection, "Exposure");

            var meiUniverseRelativePerformancesManagement = GetMeiUniverseRelativePerformances(riskRatingsData, materialEsgIssueCollection, "Management");
            var meiPeerGroupRelativePerformancesManagement = GetMeiPeerGroupRelativePerformances(riskRatingsData, materialEsgIssueCollection, "Management");
            var meiSubPeerGroupRelativePerformancesManagement = GetMeiSubPeerGroupRelativePerformances(riskRatingsData, materialEsgIssueCollection, "Management");

            foreach (var item in riskRatingsData)
            {
                var peerGroupBenchmarkRisk = peerGroupComparativeGroupRisk.GetBenchmark(item.Id);
                var subPeerGroupBenchmarkRisk = subPeerGroupComparativeGroupRisk.GetBenchmark(item.Id);
                var universeBenchmarkRisk = universeComparativeGroupRisk.GetBenchmark(item.Id);

                var marketCapPeers = marketCapitalizationComputingRisk.GetIndustryPeers(item.Id);

                var peerGroupBenchmarkExposure = peerGroupComparativeGroupExposure.GetBenchmark(item.Id);
                var subPeerGroupBenchmarkExposure = subPeerGroupComparativeGroupExposure.GetBenchmark(item.Id);
                var universeBenchmarkExposure = universeComparativeGroupExposure.GetBenchmark(item.Id);

                var peerGroupBenchmarkManagement = peerGroupComparativeGroupManagement.GetBenchmark(item.Id);
                var subPeerGroupBenchmarkManagement = subPeerGroupComparativeGroupManagement.GetBenchmark(item.Id);
                var universeBenchmarkManagement = universeComparativeGroupManagement.GetBenchmark(item.Id);

                var meiUniverseRelativePerformanceRisk = meiUniverseRelativePerformancesRisk.ContainsKey(item.Id)
                    ? meiUniverseRelativePerformancesRisk[item.Id]
                    : null;
                var meiPeerGroupRelativePerformanceRisk = meiPeerGroupRelativePerformancesRisk.ContainsKey(item.Id)
                    ? meiPeerGroupRelativePerformancesRisk[item.Id]
                    : null;
                var meiSubPeerGroupRelativePerformanceRisk = meiSubPeerGroupRelativePerformancesRisk.ContainsKey(item.Id)
                    ? meiSubPeerGroupRelativePerformancesRisk[item.Id]
                    : null;

                var meiUniverseRelativePerformanceExposure = meiUniverseRelativePerformancesExposure.ContainsKey(item.Id)
                    ? meiUniverseRelativePerformancesExposure[item.Id]
                    : null;
                var meiPeerGroupRelativePerformanceExposure = meiPeerGroupRelativePerformancesExposure.ContainsKey(item.Id)
                    ? meiPeerGroupRelativePerformancesExposure[item.Id]
                    : null;
                var meiSubPeerGroupRelativePerformanceExposure = meiSubPeerGroupRelativePerformancesExposure.ContainsKey(item.Id)
                    ? meiSubPeerGroupRelativePerformancesExposure[item.Id]
                    : null;
                var meiUniverseRelativePerformanceManagement = meiUniverseRelativePerformancesManagement.ContainsKey(item.Id)
                    ? meiUniverseRelativePerformancesManagement[item.Id]
                    : null;
                var meiPeerGroupRelativePerformanceManagement = meiPeerGroupRelativePerformancesManagement.ContainsKey(item.Id)
                    ? meiPeerGroupRelativePerformancesManagement[item.Id]
                    : null;
                var meiSubPeerGroupRelativePerformanceManagement = meiSubPeerGroupRelativePerformancesManagement.ContainsKey(item.Id)
                    ? meiSubPeerGroupRelativePerformancesManagement[item.Id]
                    : null;

                var benchmark = new RiskRatingBenchmark
                {
                    Id = item.Id,
                    MarketCapMin = marketCapPeers.MarketCapFrom,
                    MarketCapMax = marketCapPeers.MarketCapTo,

                    CompanyBenchmark = new CompanyBenchmark
                    {
                        PeersList = marketCapPeers.PeerList.Select((x, index) => Map(x, index)).ToList(),
                        IndustryPerformanceRisk = new RiskRatingRelativePerformance
                        {
                            Count = peerGroupBenchmarkRisk.Count,
                            Percentile = peerGroupBenchmarkRisk.Percentile,
                            Rank = peerGroupBenchmarkRisk.Rank,
                            Average = peerGroupBenchmarkRisk.Average,
                        },
                        SubIndustryPerformanceRisk = new RiskRatingRelativePerformance
                        {
                            Count = subPeerGroupBenchmarkRisk.Count,
                            Percentile = subPeerGroupBenchmarkRisk.Percentile,
                            Rank = subPeerGroupBenchmarkRisk.Rank,
                            Average = subPeerGroupBenchmarkRisk.Average,
                        },
                        UniversePerformanceRisk = new RiskRatingRelativePerformance
                        {
                            Count = universeBenchmarkRisk.Count,
                            Percentile = universeBenchmarkRisk.Percentile,
                            Rank = universeBenchmarkRisk.Rank,
                            Average = universeBenchmarkRisk.Average,
                        },
                        IndustryPerformanceExposure = new RiskRatingRelativePerformance
                        {
                            Count = peerGroupBenchmarkExposure.Count,
                            Percentile = peerGroupBenchmarkExposure.Percentile,
                            Rank = peerGroupBenchmarkExposure.Rank,
                            Average = peerGroupBenchmarkExposure.Average,
                        },
                        SubIndustryPerformanceExposure = new RiskRatingRelativePerformance
                        {
                            Count = subPeerGroupBenchmarkExposure.Count,
                            Percentile = subPeerGroupBenchmarkExposure.Percentile,
                            Rank = subPeerGroupBenchmarkExposure.Rank,
                            Average = subPeerGroupBenchmarkExposure.Average,
                        },
                        UniversePerformanceExposure = new RiskRatingRelativePerformance
                        {
                            Count = universeBenchmarkExposure.Count,
                            Percentile = universeBenchmarkExposure.Percentile,
                            Rank = universeBenchmarkExposure.Rank,
                            Average = universeBenchmarkExposure.Average,
                        },
                        IndustryPerformanceManagement = new RiskRatingRelativePerformance
                        {
                            Count = peerGroupBenchmarkManagement.Count,
                            Percentile = peerGroupBenchmarkManagement.Percentile,
                            Rank = peerGroupBenchmarkManagement.Rank,
                            Average = peerGroupBenchmarkManagement.Average,
                        },
                        SubIndustryPerformanceManagement = new RiskRatingRelativePerformance
                        {
                            Count = subPeerGroupBenchmarkManagement.Count,
                            Percentile = subPeerGroupBenchmarkManagement.Percentile,
                            Rank = subPeerGroupBenchmarkManagement.Rank,
                            Average = subPeerGroupBenchmarkManagement.Average,
                        },
                        UniversePerformanceManagement = new RiskRatingRelativePerformance
                        {
                            Count = universeBenchmarkManagement.Count,
                            Percentile = universeBenchmarkManagement.Percentile,
                            Rank = universeBenchmarkManagement.Rank,
                            Average = universeBenchmarkManagement.Average,
                        }
                    },

                    MaterialEsgIssueBenchmarks = GetMEIPeerList(item.Id, marketCapPeers.PeerList,
                        marketCapPeers.MarketCapFrom, marketCapPeers.MarketCapTo,
                        materialEsgIssueCollection,
                        meiUniverseRelativePerformanceRisk, meiPeerGroupRelativePerformanceRisk, meiSubPeerGroupRelativePerformanceRisk,
                        meiUniverseRelativePerformanceExposure, meiPeerGroupRelativePerformanceExposure, meiSubPeerGroupRelativePerformanceExposure,
                        meiUniverseRelativePerformanceManagement, meiPeerGroupRelativePerformanceManagement, meiSubPeerGroupRelativePerformanceManagement)
                };

                await riskRatingsBenchMarksCollection.ReplaceOneAsync(k => k.Id == benchmark.Id, benchmark, new UpdateOptions { IsUpsert = true }).ConfigureAwait(false);
            }
        }


        private static async Task GetUniverseMEIHistograms(
           IMongoCollection<PeerAggregate> riskRatingsHistogramCollection,
           IMongoCollection<RiskRatingMaterialEsgIssue> materialEsgIssueCollection,
           IMongoCollection<RiskRatingCluster> clusterCollection)
        {
            var overallClusters = clusterCollection.Find(x => x.Type == RiskRatingClusterType.MEI).SortBy(x => x.LowerBound).ToListAsync().Result;
            var lastCluster = overallClusters.LastOrDefault();

            int numberOfBuckets = 21;
            if (lastCluster != null)
                numberOfBuckets = (int)Math.Ceiling(lastCluster.UpperBound) + 1;


            var companiesWithMEI = materialEsgIssueCollection
                .Find(x => x.Type != MaterialIssueType.IdiosyncraticIssue)
                .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                    .Include(x => x.PeerGroup)
                    .Include(x => x.ExternalId)
                    .Include(x => x.ManagementRisk.UnmanagedRisk.Value))
                .ToListAsync()
                .Result;

            var histogramBuilder = Histogram<RiskRatingMaterialEsgIssue, int>
                .Create(companiesWithMEI, x => x.ExternalId, company => company.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer(), numberOfBuckets);

            foreach (var histogram in histogramBuilder.GetHistograms())
            {
                int[] clusterDataPoints = new int[overallClusters.Count];

                for (int cluterIndex = 0; cluterIndex < overallClusters.Count; cluterIndex++)
                {
                    var currentCluster = overallClusters[cluterIndex];
                    clusterDataPoints[cluterIndex] = histogram.Item2.Skip((int)currentCluster.LowerBound).Take((int)currentCluster.UpperBound - (int)currentCluster.LowerBound).Sum(x => x);
                };

                await riskRatingsHistogramCollection.InsertOneAsync(

                     new UniverseMaterialEsgIssueAggregate
                     {
                         Id = Guid.NewGuid(),
                         MEIExternalId = histogram.Item1,
                         Data = clusterDataPoints.Select((x, index) => new RiskRatingDataPoint { XValue = index, YValue = (int)Math.Round(((double)x / histogram.Item3 * 100)) })
                     })
                     .ConfigureAwait(false);
            }

        }

        private static async Task GetIndustryMEIHistograms(
           IMongoCollection<PeerAggregate> riskRatingsHistogramCollection,
           IMongoCollection<RiskRatingMaterialEsgIssue> materialEsgIssueCollection,
           IMongoCollection<RiskRatingCluster> clusterCollection,
           IEnumerable<RiskRatingResearchProfile> riskRatingsData
           )
        {
            var overallClusters = clusterCollection.Find(x => x.Type == RiskRatingClusterType.MEI).SortBy(x => x.LowerBound).ToListAsync().Result;
            var lastCluster = overallClusters.LastOrDefault();

            int numberOfBuckets = 21;
            if (lastCluster != null)
                numberOfBuckets = (int)Math.Ceiling(lastCluster.UpperBound) + 1;


            var peerGroups = riskRatingsData.GroupBy(x => x.PeerGroup).Select(x => x.Key).ToList();

            foreach (var comparativeGroup in peerGroups)
            {
                var companiesWithMEI = materialEsgIssueCollection
                    .Find(x => x.PeerGroup == comparativeGroup && x.Type != MaterialIssueType.IdiosyncraticIssue)
                    .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                        .Include(x => x.PeerGroup)
                        .Include(x => x.ExternalId)
                        .Include(x => x.ManagementRisk.UnmanagedRisk.Value))
                    .ToListAsync()
                    .Result;

                var histogramBuilder = Histogram<RiskRatingMaterialEsgIssue, int>
                    .Create(companiesWithMEI, x => x.ExternalId, company => company.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer(), numberOfBuckets);

                foreach (var histogram in histogramBuilder.GetHistograms())
                {
                    int[] clusterDataPoints = new int[overallClusters.Count];

                    for (int cluterIndex = 0; cluterIndex < overallClusters.Count; cluterIndex++)
                    {
                        var currentCluster = overallClusters[cluterIndex];
                        clusterDataPoints[cluterIndex] = histogram.Item2.Skip((int)currentCluster.LowerBound).Take((int)currentCluster.UpperBound - (int)currentCluster.LowerBound).Sum(x => x);
                    };

                    await riskRatingsHistogramCollection.InsertOneAsync(

                         new IndustryMaterialEsgIssueAggregate
                         {
                             Id = Guid.NewGuid(),
                             Industry = comparativeGroup,
                             MEIExternalId = histogram.Item1,
                             Data = clusterDataPoints.Select((x, index) => new RiskRatingDataPoint { XValue = index, YValue = (int)Math.Round(((double)x / histogram.Item3 * 100)) })
                         })
                         .ConfigureAwait(false);
                }
            }
        }

        private static async Task GetSubIndustryMEIHistograms(
            IMongoCollection<PeerAggregate> riskRatingsHistogramCollection,
            IMongoCollection<RiskRatingMaterialEsgIssue> materialEsgIssueCollection,
            IMongoCollection<RiskRatingCluster> clusterCollection,
            IEnumerable<RiskRatingResearchProfile> riskRatingsData)
        {
            var overallClusters = clusterCollection.Find(x => x.Type == RiskRatingClusterType.MEI).SortBy(x => x.LowerBound).ToListAsync().Result;
            var lastCluster = overallClusters.LastOrDefault();

            int numberOfBuckets = 21;
            if (lastCluster != null)
                numberOfBuckets = (int)Math.Ceiling(lastCluster.UpperBound) + 1;


            var subPeerGroupIds = riskRatingsData.GroupBy(x => x.SubPeerGroupId).Select(x => x.Key).ToList();

            foreach (var comparativeGroup in subPeerGroupIds)
            {
                var companiesWithMEI = materialEsgIssueCollection
                    .Find(x => x.SubPeerGroupId == comparativeGroup && x.Type != MaterialIssueType.IdiosyncraticIssue)
                    .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                        .Include(x => x.SubPeerGroupId)
                        .Include(x => x.ExternalId)
                        .Include(x => x.ManagementRisk.UnmanagedRisk.Value))
                    .ToListAsync()
                    .Result;

                var histogramBuilder = Histogram<RiskRatingMaterialEsgIssue, int>
                    .Create(companiesWithMEI, x => x.ExternalId, company => company.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer(), numberOfBuckets);

                foreach (var histogram in histogramBuilder.GetHistograms())
                {
                    int[] clusterDataPoints = new int[overallClusters.Count];

                    for (int cluterIndex = 0; cluterIndex < overallClusters.Count; cluterIndex++)
                    {
                        var currentCluster = overallClusters[cluterIndex];
                        clusterDataPoints[cluterIndex] = histogram.Item2.Skip((int)currentCluster.LowerBound).Take((int)currentCluster.UpperBound - (int)currentCluster.LowerBound).Sum(x => x);
                    };

                    await riskRatingsHistogramCollection.InsertOneAsync(

                         new SubIndustryMaterialEsgIssueAggregate
                         {
                             Id = Guid.NewGuid(),
                             SubIndustryId = comparativeGroup,
                             MEIExternalId = histogram.Item1,
                             Data = clusterDataPoints.Select((x, index) => new RiskRatingDataPoint { XValue = index, YValue = (int)Math.Round(((double)x / histogram.Item3 * 100)) })
                         })
                         .ConfigureAwait(false);
                }
            }
        }


        private static IEnumerable<MaterialEsgIssuesBenchmark> GetMEIPeerList(Guid CompanyId, List<RiskRatingResearchProfile> peerList,
            double min, double max,
            IMongoCollection<RiskRatingMaterialEsgIssue> materialEsgIssueCollection,
            Dictionary<int, RiskRatingRelativePerformance> meiUniverseRelativePerformanceRisk,
            Dictionary<int, RiskRatingRelativePerformance> meiPeerGroupRelativePerformanceRisk,
            Dictionary<int, RiskRatingRelativePerformance> meiSubPeerGroupRelativePerformanceRisk,
            Dictionary<int, RiskRatingRelativePerformance> meiUniverseRelativePerformanceExposure,
            Dictionary<int, RiskRatingRelativePerformance> meiPeerGroupRelativePerformanceExposure,
            Dictionary<int, RiskRatingRelativePerformance> meiSubPeerGroupRelativePerformanceExposure,
            Dictionary<int, RiskRatingRelativePerformance> meiUniverseRelativePerformanceManagement,
            Dictionary<int, RiskRatingRelativePerformance> meiPeerGroupRelativePerformanceManagement,
            Dictionary<int, RiskRatingRelativePerformance> meiSubPeerGroupRelativePerformanceManagement)
        {
            var companyMEIs = materialEsgIssueCollection
                .Find(x => x.CompanyId == CompanyId && x.Type != MaterialIssueType.IdiosyncraticIssue)
                 .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                    .Include(x => x.ExternalId)
                    .Include(x => x.Name))
                .ToListAsync()
                .Result;

            var peerListIdsRisk = peerList.Select(x => x.Id);

            var PeerListScoreRisk = materialEsgIssueCollection
                .Find(x => peerListIdsRisk.Contains(x.CompanyId) && x.Type != MaterialIssueType.IdiosyncraticIssue)
                .Project<RiskRatingMaterialEsgIssue>(Builders<RiskRatingMaterialEsgIssue>.Projection
                    .Include(x => x.ExternalId)
                    .Include(x => x.CompanyId)
                    .Include(x => x.ManagementRisk.UnmanagedRisk.Value)
                    .Include(x => x.ManagementRisk.Management)
                    .Include(x => x.ManagementRisk.Exposure)
                    .Include(x => x.Cluster.Assessment))
                .ToListAsync()
                .Result;

            foreach (var companyMEI in companyMEIs)
            {
                var companyPeers = PeerListScoreRisk.Where(x => x.ExternalId == companyMEI.ExternalId);

                var universePerformanceRisk = meiUniverseRelativePerformanceRisk.ContainsKey(companyMEI.ExternalId)
                    ? meiUniverseRelativePerformanceRisk[companyMEI.ExternalId]
                    : null;

                var industryPerformanceRisk = meiPeerGroupRelativePerformanceRisk.ContainsKey(companyMEI.ExternalId)
                    ? meiPeerGroupRelativePerformanceRisk[companyMEI.ExternalId]
                    : null;

                var subIndustryPerformanceRisk = meiSubPeerGroupRelativePerformanceRisk.ContainsKey(companyMEI.ExternalId)
                    ? meiSubPeerGroupRelativePerformanceRisk[companyMEI.ExternalId]
                    : null;
                var universePerformanceExposure = meiUniverseRelativePerformanceExposure.ContainsKey(companyMEI.ExternalId)
                    ? meiUniverseRelativePerformanceExposure[companyMEI.ExternalId]
                    : null;

                var industryPerformanceExposure = meiPeerGroupRelativePerformanceExposure.ContainsKey(companyMEI.ExternalId)
                    ? meiPeerGroupRelativePerformanceExposure[companyMEI.ExternalId]
                    : null;

                var subIndustryPerformanceExposure = meiSubPeerGroupRelativePerformanceExposure.ContainsKey(companyMEI.ExternalId)
                    ? meiSubPeerGroupRelativePerformanceExposure[companyMEI.ExternalId]
                    : null;
                var universePerformanceManagement = meiUniverseRelativePerformanceManagement.ContainsKey(companyMEI.ExternalId)
                    ? meiUniverseRelativePerformanceManagement[companyMEI.ExternalId]
                    : null;

                var industryPerformanceManagement = meiPeerGroupRelativePerformanceManagement.ContainsKey(companyMEI.ExternalId)
                    ? meiPeerGroupRelativePerformanceManagement[companyMEI.ExternalId]
                    : null;

                var subIndustryPerformanceManagement = meiSubPeerGroupRelativePerformanceManagement.ContainsKey(companyMEI.ExternalId)
                    ? meiSubPeerGroupRelativePerformanceManagement[companyMEI.ExternalId]
                    : null;

                var item = new MaterialEsgIssuesBenchmark
                {
                    MeiExternalId = companyMEI.ExternalId,
                    MeiName = companyMEI.Name,
                    MarketCapMin = min,
                    MarketCapMax = max,
                    PeersList = peerList.Select((x) =>
                    {
                        var companyPeer = companyPeers.FirstOrDefault(c => c.CompanyId == x.Id);
                        return new RiskRatingPeer
                        {
                            CompanyId = x.Id,
                            CompanyName = x.CompanyName,
                            UnmanagedRisk = companyPeer != null ? (double?)companyPeer.ManagementRisk.UnmanagedRisk.Value : null,
                            ManagementRisk = companyPeer != null ? (double?)companyPeer.ManagementRisk.Management.Value : null,
                            Assessment = companyPeer != null ? (RiskRatingClusterAssessment?)companyPeer.Cluster.Assessment : null,
                            Exposure = companyPeer != null ? (double?)companyPeer.ManagementRisk.Exposure.Value : null
                        };
                    })
                    .OrderBy(x => x.UnmanagedRisk).ToList(),
                    UniversePerformanceRisk = universePerformanceRisk,
                    IndustryPerformanceRisk = industryPerformanceRisk,
                    SubIndustryPerformanceRisk = subIndustryPerformanceRisk,
                    UniversePerformanceExposure = universePerformanceExposure,
                    IndustryPerformanceExposure = industryPerformanceExposure,
                    SubIndustryPerformanceExposure = subIndustryPerformanceExposure,
                    UniversePerformanceManagement = universePerformanceManagement,
                    IndustryPerformanceManagement = industryPerformanceManagement,
                    SubIndustryPerformanceManagement = subIndustryPerformanceManagement
                };

                var index = 1;
                item.PeersList.ForEach((x) => { x.Rank = index++; });

                yield return item;
            }
        }


        private static async Task GetPeerGroupHistograms(IMongoCollection<PeerAggregate> riskRatingsHistogramCollection,
            IMongoCollection<RiskRatingCluster> clusterCollection, List<RiskRatingResearchProfile> riskRatingsData)
        {
            var overallClusters = clusterCollection.Find(x => x.Type == RiskRatingClusterType.Overall).ToListAsync().Result;
            var numberOfBuckets = 101;
            if (overallClusters.Last() != null)
                numberOfBuckets = (int)Math.Ceiling(overallClusters.Last().UpperBound) + 1;


            var peerGroupHistogramBuilder = Histogram<RiskRatingResearchProfile, string>
                .Create(riskRatingsData, x => x.PeerGroup, x => x.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer(), numberOfBuckets);

            var peerGroupDataPoints = peerGroupHistogramBuilder.GetHistograms();

            foreach (var histogram in peerGroupDataPoints)
            {
                int[] clusterDataPoints = new int[overallClusters.Count];

                for (int cluterIndex = 0; cluterIndex < overallClusters.Count; cluterIndex++)
                {
                    var currentCluster = overallClusters[cluterIndex];
                    clusterDataPoints[cluterIndex] = histogram.Item2.Skip((int)currentCluster.LowerBound).Take((int)currentCluster.UpperBound - (int)currentCluster.LowerBound).Sum(x => x);
                };

                var peerGroupHistogram = new IndustryAggregate
                {
                    Id = Guid.NewGuid(),
                    Industry = histogram.Item1,
                    Data = clusterDataPoints.Select((x, index) => new RiskRatingDataPoint { XValue = index, YValue = (int)Math.Round(((double)x / histogram.Item3 * 100)) })
                };

                await riskRatingsHistogramCollection.InsertOneAsync(peerGroupHistogram).ConfigureAwait(false);
            }


            var subPeerGroupHistogramBuilder = Histogram<RiskRatingResearchProfile, int>
                .Create(riskRatingsData, x => x.SubPeerGroupId, x => x.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer(), numberOfBuckets);

            var subPeerGroupDataPoints = subPeerGroupHistogramBuilder.GetHistograms();

            foreach (var histogram in subPeerGroupDataPoints)
            {
                int[] clusterDataPoints = new int[overallClusters.Count];

                for (int cluterIndex = 0; cluterIndex < overallClusters.Count; cluterIndex++)
                {
                    var currentCluster = overallClusters[cluterIndex];
                    clusterDataPoints[cluterIndex] = histogram.Item2.Skip((int)currentCluster.LowerBound).Take((int)currentCluster.UpperBound - (int)currentCluster.LowerBound).Sum(x => x);
                };

                var peerGroupHistogram = new SubIndustryAggregate
                {
                    Id = Guid.NewGuid(),
                    SubIndustryId = histogram.Item1,
                    Data = clusterDataPoints.Select((x, index) => new RiskRatingDataPoint { XValue = index, YValue = (int)Math.Round(((double)x / histogram.Item3 * 100)) })
                };

                await riskRatingsHistogramCollection.InsertOneAsync(peerGroupHistogram).ConfigureAwait(false);
            }
        }

        private static async Task GetUniverseHistogram(IMongoCollection<PeerAggregate> riskRatingsHistogramCollection,
            IMongoCollection<RiskRatingCluster> clusterCollection, List<RiskRatingResearchProfile> riskRatingsData)
        {
            var overallClusters = clusterCollection.Find(x => x.Type == RiskRatingClusterType.Overall).ToListAsync().Result;
            var numberOfBuckets = 101;
            if (overallClusters.Last() != null)
                numberOfBuckets = (int)Math.Ceiling(overallClusters.Last().UpperBound) + 1;

            var universeHistogramBuilder = Histogram<RiskRatingResearchProfile, int>.Create(riskRatingsData, null, x => x.ManagementRisk.UnmanagedRisk.Value, new DoubleComparer(), numberOfBuckets);

            var dataPoints = universeHistogramBuilder.GetDataPoints();
            Tuple<int[], int> clusterDataPoints = new Tuple<int[], int>(new int[overallClusters.Count], dataPoints.Item2);
            for (int cluterIndex = 0; cluterIndex < overallClusters.Count; cluterIndex++)
            {
                var currentCluster = overallClusters[cluterIndex];
                clusterDataPoints.Item1[cluterIndex] = dataPoints.Item1.Skip((int)currentCluster.LowerBound).Take((int)currentCluster.UpperBound - (int)currentCluster.LowerBound).Sum(x => x);
            };

            await riskRatingsHistogramCollection.InsertOneAsync(new UniverseAggregate
            {
                Id = Guid.NewGuid(),
                Data = clusterDataPoints.Item1.Select((x, index) => new RiskRatingDataPoint
                {
                    XValue = index,
                    YValue = (int)Math.Round(((double)x / dataPoints.Item2 * 100), MidpointRounding.AwayFromZero)
                })
            })
            .ConfigureAwait(false);
        }

        private static RiskRatingPeer Map(RiskRatingResearchProfile item, int index = 0)
        {
            return new RiskRatingPeer
            {
                CompanyId = item.Id,
                CompanyName = item.CompanyName,
                UnmanagedRisk = item.ManagementRisk.UnmanagedRisk.Value,
                ManagementRisk = item.ManagementRisk.Management.Value,
                Assessment = item.Cluster.Assessment,
                Exposure = item.ManagementRisk.Exposure.Value,
                Rank = index + 1
            };
        }

        private async Task LogExecutionTimeAsync(string message, Func<Task> action)
        {
            _syncContext.Log("STARTING - " + message);
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            await action.Invoke();
            stopwatch.Stop();
            _syncContext.Log("FINISHED - " + message + " in " + stopwatch.Elapsed);
        }
    }

    class RiskRatingResearchProfileComparer : IEqualityComparer<RiskRatingResearchProfile>
    {
        bool IEqualityComparer<RiskRatingResearchProfile>.Equals(RiskRatingResearchProfile x, RiskRatingResearchProfile y)
        {
            return x.Id == y.Id;
        }

        int IEqualityComparer<RiskRatingResearchProfile>.GetHashCode(RiskRatingResearchProfile obj)
        {
            return obj.Id.GetHashCode();
        }
    }
}
